package com.liu.activiti.utils;

import com.liu.activiti.pojo.activiti.dto.ActTaskDTO;
import com.liu.activiti.pojo.activiti.dto.DefinitionIdDTO;
import com.liu.activiti.pojo.activiti.dto.PageDomain;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.activiti.api.runtime.shared.query.Page;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.EndEvent;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Component
@AllArgsConstructor
@Slf4j
public class ActivitiUtils {
    public final TaskService taskService;

    public final RuntimeService runtimeService;

    public final HistoryService historyService;

    public final RepositoryService repositoryService;


    /**
     * 开始流程
     *
     * @param businessKey          业务主键
     * @param processDefinitionKey 流程定义key
     * @param title                流程名称
     * @param variables            流程变量 (用来设置 流程中的 表达式)
     * @return
     */
    public synchronized ProcessInstance startProcess(String businessKey, String processDefinitionKey, String title, Map<String, Object> variables) {
        //判断流程是否已开启
        if (!peddingTaskList(businessKey, null, null).isEmpty()) {
            throw new RuntimeException("该业务主键已启动流程,不可重复启动!");
        }
        return runtimeService.createProcessInstanceBuilder()
                .processDefinitionKey(processDefinitionKey)
                .businessKey(businessKey)
                .name(title)
                .variables(variables)
                .start();
    }


    /**
     * 根据业务主键查询待办任务
     *
     * @param businessKey
     * @param assignee
     * @param processInstanceId
     * @return
     */
    public List<Task> peddingTaskList(String businessKey, String assignee, String processInstanceId) {
        //查询接口
        TaskQuery taskQuery = taskService.createTaskQuery();

        //任务指定用户人
        if (org.apache.commons.lang3.StringUtils.isNotBlank(assignee)) {
            taskQuery.taskAssignee(assignee);
        }
        //业务标识
        if (org.apache.commons.lang3.StringUtils.isNotBlank(businessKey)) {
            taskQuery.processInstanceBusinessKey(businessKey);
        }
        //流程实例ID
        if (StringUtils.isNotBlank(processInstanceId)) {
            taskQuery.processInstanceId(processInstanceId);
        }
        List<Task> list = taskQuery.list();
        log.info("查询原生流程返回结果list:{}", list);
        return list;
    }

    /**
     * 任务完成
     *
     * @param businessKey       业务id
     * @param processInstanceId 流程实例ID
     * @param assignee          用户人
     */
    public void completeTask(String businessKey, String assignee, String processInstanceId, Map<String, Object> variables) {
        if (StringUtils.isEmpty(businessKey) || StringUtils.isEmpty(assignee) || StringUtils.isEmpty(processInstanceId)) {
            throw new RuntimeException("业务主键,用户人,流程实例ID不能为空");
        }
        List<Task> tasks = peddingTaskList(businessKey, assignee, processInstanceId);
        //理论上 绑定业务id的情况下 只有一个代办任务
        Task task = tasks.get(0);
        //完成任务
        if (variables.isEmpty()) {
            taskService.complete(task.getId());
        } else {
            taskService.complete(task.getId(), variables);
        }

    }

    /**
     * 分页查询 代办任务
     *
     * @param pageDomain
     * @return
     */
//    public Page<ActTaskDTO> selectPageList(PageDomain pageDomain, String userId) {
//        Page<ActTaskDTO> list = new Page<ActTaskDTO>();
//
//        TaskQuery taskQuery = taskService.createTaskQuery().taskAssignee(userId);
//        List<Task> tasks = taskQuery.listPage((pageDomain.getPageNum() - 1) * pageDomain.getPageSize(), pageDomain.getPageSize());
//        long totalItems = taskQuery.count();
//        list.setTotal(totalItems);
//        if (totalItems != 0) {
//            Set<String> processInstanceIdIds = tasks.parallelStream().map(t -> t.getProcessInstanceId()).collect(Collectors.toSet());
//            List<ProcessInstance> processInstanceList = runtimeService.createProcessInstanceQuery().processInstanceIds(processInstanceIdIds).list();
//            List<ActTaskDTO> actTaskDTOS = tasks.stream()
//                    .map(t -> new ActTaskDTO(t, processInstanceList.parallelStream().filter(pi -> t.getProcessInstanceId().equals(pi.getId())).findAny().get()))
//                    .collect(Collectors.toList());
//            list.addAll(actTaskDTOS);
//
//        }
//        return list;
//    }


    /**
     * 根据名称退回
     */
    public void revoke(String processInstanceId, String nowUser) throws Exception {
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
        if (task == null) {
            throw new Exception("流程未启动或已执行完成，无法撤回");
        }
        //通过processInstanceId查询历史节点
        List<HistoricTaskInstance> htiList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByTaskCreateTime()
                .asc()
                .list();
        String myTaskId = null;
        HistoricTaskInstance myTask = null;
        //找到当前运行的节点
        for (HistoricTaskInstance hti : htiList) {
            if (nowUser.equals(hti.getAssignee())) {
                myTaskId = hti.getId();
                myTask = hti;
                break;
            }
        }
        if (null == myTaskId) {
            throw new Exception("该任务非当前用户提交，无法撤回");
        }
        String processDefinitionId = myTask.getProcessDefinitionId();
        //获取流程模型
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        String myActivityId = null;
        //查询已经完成的流程节点，查询到上一条已完成的节点，则跳出循环
        List<HistoricActivityInstance> haiList = historyService.createHistoricActivityInstanceQuery()
                .executionId(myTask.getExecutionId()).finished().list();
        for (HistoricActivityInstance hai : haiList) {
            if (myTaskId.equals(hai.getTaskId())) {
                myActivityId = hai.getActivityId();
                break;
            }
        }
        FlowNode myFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(myActivityId);
        Execution execution = runtimeService.createExecutionQuery().executionId(task.getExecutionId()).singleResult();
        String activityId = execution.getActivityId();
        FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityId);
        //记录原活动方向
        List<SequenceFlow> oriSequenceFlows = new ArrayList<SequenceFlow>();
        oriSequenceFlows.addAll(flowNode.getOutgoingFlows());
        //清理活动方向
        flowNode.getOutgoingFlows().clear();
        //建立新方向
        List<SequenceFlow> newSequenceFlowList = new ArrayList<SequenceFlow>();
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("newSequenceFlowId");
        newSequenceFlow.setSourceFlowElement(flowNode);
        newSequenceFlow.setTargetFlowElement(myFlowNode);
        newSequenceFlowList.add(newSequenceFlow);
        flowNode.setOutgoingFlows(newSequenceFlowList);
        Authentication.setAuthenticatedUserId(nowUser);
        taskService.addComment(task.getId(), task.getProcessInstanceId(), "撤回");
        //完成任务
        taskService.complete(task.getId());
        //恢复原方向
        flowNode.setOutgoingFlows(oriSequenceFlows);
        log.info("退回成功！");
    }


    /**
     * 删除整个流程实例 包含创建的任务
     *
     * @param processInstanceId
     */
    public void deleteTasksByProcessInstanceId(String processInstanceId) {
        runtimeService.deleteProcessInstance(processInstanceId, "撤销流程");
    }

    /**
     * 触发 结束事件 完成当前任务并把指针指向结束任务
     *
     * @param processInstanceId
     */
    public void endTasksByProcessInstanceId(String processInstanceId) {
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        List endEventList = bpmnModel.getMainProcess().findFlowElementsOfType(EndEvent.class);
        FlowNode endFlowNode = (FlowNode) endEventList.get(0);
        FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey());

        //  临时保存当前活动的原始方向
        List originalSequenceFlowList = new ArrayList<>();
        originalSequenceFlowList.addAll(currentFlowNode.getOutgoingFlows());
        //  清理活动方向
        currentFlowNode.getOutgoingFlows().clear();

        //建立新方向
        List<SequenceFlow> newSequenceFlowList = new ArrayList<SequenceFlow>();
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("newSequenceFlowId");
        newSequenceFlow.setSourceFlowElement(currentFlowNode);
        newSequenceFlow.setTargetFlowElement(endFlowNode);
        newSequenceFlowList.add(newSequenceFlow);
        currentFlowNode.setOutgoingFlows(newSequenceFlowList);

        taskService.addComment(task.getId(), task.getProcessInstanceId(), "撤回");
        //完成任务
        taskService.complete(task.getId());

    }


    /**
     * 获取高亮方法
     *
     * @param instanceId
     * @return
     */
    public DefinitionIdDTO getDefinitionsByInstanceId(String instanceId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).singleResult();
        String deploymentId = processInstance.getDeploymentId();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
        return new DefinitionIdDTO(processDefinition);
    }
}


